﻿using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PloobsEngine;
using PloobsEngine.Cameras;
using PloobsEngine.Draw;
using PloobsEngine.Entity;
using PloobsEngine.Input;
using PloobsEngine.Light;
using PloobsEngine.Material;
using PloobsEngine.MessageSystem;
using PloobsEngine.Modelo;
using PloobsEngine.Physics;
using PloobsEngine.Physics.Bepu;
using PloobsEngine.SceneControl;
using PloobsEngine.Utils;
using PloobsEngineDemo3;

namespace Etapa1.Screens
{    
    /// <summary>
    /// Objects Manipulation Screen
    /// Exemplos Mensagem e deteccao de colisao
    /// </summary>
    public class ObjectsManipulationScreen : SceneScreen
    {
        private BepuPhysicWorld physicWorld;
        private BrutalForceWorld mundo;
        private EngineStuff engine;        
        ICamera cam;

        public ObjectsManipulationScreen(EngineStuff es)
        {
            this.engine = es;
            physicWorld = new BepuPhysicWorld();
            mundo = new BrutalForceWorld(physicWorld);                                    
        }
        
        public override void LoadContent(EngineStuff es)
        {            
            InputAdvanced inp = new InputAdvanced(engine);
            engine.AddComponent(inp);

            MessageDeliver md = new MessageDeliver();
            engine.AddComponent(md);

            base.LoadContent(es);            

            #region Models
            ///Paralelepipelo (cubo com scale ) enviara mensagens quando colidir com objetos
            {
                SimpleModel sm = new SimpleModel("..\\Content\\Model\\cubo");
                sm.SetTexture(TextureCreator.CreateColorTexture(1, Color.White), TextureType.DIFFUSE);
                sm.LoadModelo();
                BoxObject pi = new BoxObject(new Vector3(100, 20, 0), 1, 5,new Vector3(100, 5, 100));                
                ///Adiciona um handler que sera chamada quando uma colisao acontecer
                pi.Entity.EventManager.InitialCollisionDetected += new BEPUphysics.Events.InitialCollisionDetectedEventHandler(EventManager_InitialCollisionDetected);
                NormalDeferred shader = new NormalDeferred();                
                shader.Initialize();                
                IMaterial mat = new Material(shader);
                IObject obj3 = new SimpleObject(mat, pi, sm);                
                mundo.AddObject(obj3);
            }

            ////CUBO Q VAI MUDAR DE COR
            {
                SimpleModel sm = new SimpleModel("..\\Content\\Model\\cubo");
                sm.SetTexture(TextureCreator.CreateColorTexture(1,Color.Yellow), TextureType.DIFFUSE);
                sm.LoadModelo();
                BoxObject pi = new BoxObject(new Vector3(50, 50, 50), 1, 5, new Vector3(15));
                pi.isMotionLess = true;                
                NormalDeferred shader = new NormalDeferred();
                shader.Initialize();
                IMaterial mat = new Material(shader);
                IObject obj3 = new SimpleObject(mat, pi, sm);
                ///Adiciona um handler para tratar das mensagens (existe outra maneira mais robusta de fazer isto, conforme citado no exemplo sobre Triggers)
                obj3.OnRecieveMessage += new OnRecieveMessage(obj3_OnRecieveMessage);
                ///Forcando um ID, normalmente ele eh setado automaticamente ao adicionar o objeto no mundo
                obj3.setId(77);
                mundo.AddObject(obj3);
                int id = obj3.getId();
                ///Testa se o Id atribuido eh o forcado 
                ///Internamente a Engine atribui Ids acima de 1000 (valores abaixo funcionarao, a menos que alguem ja tenha forcado este Id antes)                
                ///Como neste caso nao forcamos o id de ninguem para 77, entao o obj3 tera id 77
                ///Soh pra garantir ;)
                Debug.Assert(id == 77);
             
            }

            ////cubo que escuta um canal de mensagens
            {
                SimpleModel sm = new SimpleModel("..\\Content\\Model\\cubo");
                sm.SetTexture(TextureCreator.CreateColorTexture(1,Color.Red), TextureType.DIFFUSE);
                sm.LoadModelo();
                BoxObject pi = new BoxObject(new Vector3(100, 50, 50), 1, 5, new Vector3(15));
                pi.isMotionLess = true;
                NormalDeferred shader = new NormalDeferred();
                shader.Initialize();
                IMaterial mat = new Material(shader);
                IObject obj3 = new SimpleObject(mat, pi, sm);
                ///Adiciona um handler para tratar das mensagens (existe outra maneira mais robusta de fazer isto, conforme citado no exemplo sobre Triggers)
                ///OBSERVAR QUE FOI USADO O MESMO HANDLER QUE O OBJETO ANTERIOR (JA QUE DESEJA-SE TER O MESMO EFEITO)
                obj3.OnRecieveMessage += new OnRecieveMessage(obj3_OnRecieveMessage);                
                mundo.AddObject(obj3);                

                ///Adiciona este objeto ao canal de comunicao chamado "cubo" (recebera mensagens deste grupo tb)
                EntityMapper.getInstance().AddgrouptagRecieveEntity("cubo", obj3);
            }


            {
                SimpleModel sm = new SimpleModel("..\\Content\\Model\\cenario");
                sm.LoadModelo();
                IPhysicObject pi = new TriangleMeshObject(Vector3.Zero, sm);
                NormalDeferred shader = new NormalDeferred();                
                shader.Initialize();
                IMaterial mat = new Material(shader);
                IObject obj3 = new SimpleObject(mat, pi, sm);
                mundo.AddObject(obj3);
            }            
            

            #endregion            

            cam = new CameraFirstPerson(true);            
            cam.FarPlane = 3000;

            LightThrowBepu lt = new LightThrowBepu(mundo, engine);         

            #region NormalLight
            DirectionalLight ld1 = new DirectionalLight(Vector3.Left, Color.White);
            DirectionalLight ld2 = new DirectionalLight(Vector3.Right, Color.White);
            DirectionalLight ld3 = new DirectionalLight(Vector3.Backward, Color.White);
            DirectionalLight ld4 = new DirectionalLight(Vector3.Forward, Color.White);
            DirectionalLight ld5 = new DirectionalLight(Vector3.Down, Color.White);
            float li = 0.4f;
            ld1.LightIntensity = li;
            ld2.LightIntensity = li;
            ld3.LightIntensity = li;
            ld4.LightIntensity = li;
            ld5.LightIntensity = li;
            mundo.AddLight(ld1);
            mundo.AddLight(ld2);
            mundo.AddLight(ld3);
            mundo.AddLight(ld4);
            mundo.AddLight(ld5);
            #endregion

            ////Criacao de uma Tecnica
            DeferredRenderTechnic dr = new DeferredRenderTechnic(engine);
            
            IRenderTechnic[] rt = new IRenderTechnic[] { dr };
            this.RenderTechnics = rt;
            mundo.AddCamera(cam);
            this.World = mundo;

            Drawing.ClearEveryFrame = false;
        }


        /// <summary>
        /// Handler que trata das mensagens recebidas pelos dois cubos
        /// </summary>
        /// <param name="Reciever"></param>
        /// <param name="mes"></param>
        void obj3_OnRecieveMessage(IObject Reciever, Message mes)
        {
            if (mes.Cod == "CHANGECOLOR")
            {
                ///Muda a Textura do objeto
                Texture2D tex = TextureCreator.CreateColorTexture(1, StaticRandom.RandomColor());
                (Reciever.Modelo as SimpleModel).SetTexture(tex, TextureType.DIFFUSE);

                ///Recuperar quem enviou a mensagem 
                int senderId = mes.Sender;
                if (senderId != PrincipalConstants.InvalidId)
                {
                    if (mes.SenderType == SenderType.OBJECT)
                    {
                        IObject sender = (EntityMapper.getInstance().getEntity(senderId) as IObject);
                    }

                }
            }
        }


        /// <summary>
        /// O movimento da bola nao eh completamente continuo em cima do paralelepipedo, 
        /// entao a bola fica subindo e descendo e a cada movimento deste uma nova colisao eh criada
        /// Para evitar isto, apenas a primeira iteracao das bolas com a superficies sera considerada
        /// </summary>
        List<int> alreadProcessed = new List<int>();

        /// <summary>
        /// Handler que trata das colisoes com o paralelepipedo
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="other"></param>
        /// <param name="collisionPair"></param>
        void EventManager_InitialCollisionDetected(BEPUphysics.Entities.Entity sender, BEPUphysics.Entities.Entity other, BEPUphysics.CollisionPair collisionPair)
        {            
            
            IObject send = BepuEntityObject.RecoverObjectFromEntity(sender);
            IObject obj = BepuEntityObject.RecoverObjectFromEntity(other);

            ///Verifica se esta bola ja foi considerada
            if (alreadProcessed.Contains(obj.getId()))
                return;
            alreadProcessed.Add(obj.getId());
            
            ///se o objeto colidido for diferente do cenario 
            if (obj.PhysicObject.PhysicObjectTypes != PhysicObjectTypes.TRIANGLEMESHOBJECT)
            {
                Drawing.ClearAll();
                Drawing.Draw2dTextAt2dLocation("Collision With Object " + obj.Name, new Vector2(20, 20), Color.White);                                
                
                ///Envia uma mensagem para o canal de comunicacao CUBO
                Message m = new Message(send.getId(), PrincipalConstants.InvalidId, "cubo", Priority.MEDIUM, -1, SenderType.OBJECT, null, "CHANGECOLOR");
                MessageDeliver.SendMessage(m);

                ///Esta mensagem foi enviada sem Sender (Quem receber a mensagem nao sabera quem enviou)
                ///Envia uma mensagem para o "CUBO QUE VAI MUDAR DE COR" (lembre que o id dele eh 77 !!)
                m = new Message(PrincipalConstants.InvalidId, 77, null, Priority.MEDIUM, -1, SenderType.OBJECT, null, "CHANGECOLOR");
                MessageDeliver.SendMessage(m);
            }
        }
    }
}

